/*
 * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Unlicense OR CC0-1.0
 */
/*  WiFi softAP & station Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_mac.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif_net_stack.h"
#include "esp_netif.h"
//#include "nvs_flash.h"
#include "lwip/inet.h"
#include "lwip/netdb.h"
#include "lwip/sockets.h"
#if IP_NAPT
#include "lwip/lwip_napt.h"
#endif
#include "lwip/err.h"
#include "lwip/sys.h"

#include "esp_http_client.h"
//#include "cJSON.h"

/* The examples use WiFi configuration that you can set via project configuration menu.

   If you'd rather not, just change the below entries to strings with
   the config you want - ie #define EXAMPLE_ESP_WIFI_STA_SSID "mywifissid"
*/

#define TAG "Wifi"
/* STA Configuration */
#define EXAMPLE_ESP_WIFI_STA_SSID           "HUAWEI-KS4MCE"
#define EXAMPLE_ESP_WIFI_STA_PASSWD         "19970512"
#define EXAMPLE_ESP_MAXIMUM_RETRY           5
#define CONFIG_ESP_WIFI_AUTH_WPA2_PSK       1

#if CONFIG_ESP_WIFI_AUTH_OPEN
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD   WIFI_AUTH_OPEN
#elif CONFIG_ESP_WIFI_AUTH_WEP
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD   WIFI_AUTH_WEP
#elif CONFIG_ESP_WIFI_AUTH_WPA_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD   WIFI_AUTH_WPA_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA2_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD   WIFI_AUTH_WPA2_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA_WPA2_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD   WIFI_AUTH_WPA_WPA2_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA3_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD   WIFI_AUTH_WPA3_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA2_WPA3_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD   WIFI_AUTH_WPA2_WPA3_PSK
#elif CONFIG_ESP_WIFI_AUTH_WAPI_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD   WIFI_AUTH_WAPI_PSK
#endif

extern esp_err_t start_web_server();

unsigned char wifi_ssid[32];
unsigned char wifi_password[64];

/* AP Configuration */
#define EXAMPLE_ESP_WIFI_AP_SSID            "myssid"
#define EXAMPLE_ESP_WIFI_AP_PASSWD          "mypassword"
#define EXAMPLE_ESP_WIFI_CHANNEL            1
#define EXAMPLE_MAX_STA_CONN                4


/* The event group allows multiple bits for each event, but we only care about two events:
 * - we are connected to the AP with an IP
 * - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0             // set when connected to AP
#define WIFI_FAIL_BIT      BIT1             // set when failed to connect after max retries

static const char *TAG_AP = "WiFi SoftAP";
static const char *TAG_STA = "WiFi Sta";
static int s_retry_num = 0;

/* FreeRTOS event group to signal when we are connected/disconnected */
static EventGroupHandle_t s_wifi_event_group;

static void wifi_event_handler(void *arg, esp_event_base_t event_base,
                               int32_t event_id, void *event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STACONNECTED) {
        wifi_event_ap_staconnected_t *event = (wifi_event_ap_staconnected_t *) event_data;
        ESP_LOGI(TAG_AP, "Station "MACSTR" joined, AID=%d",
                 MAC2STR(event->mac), event->aid);
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STADISCONNECTED) {
        wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t *) event_data;
        ESP_LOGI(TAG_AP, "Station "MACSTR" left, AID=%d, reason:%d",
                 MAC2STR(event->mac), event->aid, event->reason);
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        esp_wifi_connect();
        ESP_LOGI(TAG_STA, "Station started");
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
        ESP_LOGI(TAG_STA, "Got IP:" IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

/* Initialize soft AP */
esp_netif_t *wifi_init_softap(void)
{
    esp_netif_t *esp_netif_ap = esp_netif_create_default_wifi_ap();

    wifi_config_t wifi_ap_config = {
        .ap = {
            .ssid = EXAMPLE_ESP_WIFI_AP_SSID,
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_AP_SSID),
            .channel = EXAMPLE_ESP_WIFI_CHANNEL,
            .password = EXAMPLE_ESP_WIFI_AP_PASSWD,
            .max_connection = EXAMPLE_MAX_STA_CONN,
            .authmode = WIFI_AUTH_WPA_WPA2_PSK, //WIFI_AUTH_WPA2_PSK,
            .pmf_cfg = {
                .required = false,
            },
        },
    };

    if (strlen(EXAMPLE_ESP_WIFI_AP_PASSWD) == 0) {
        wifi_ap_config.ap.authmode = WIFI_AUTH_OPEN;
    }

    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_ap_config));

    ESP_LOGI(TAG_AP, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
             EXAMPLE_ESP_WIFI_AP_SSID, EXAMPLE_ESP_WIFI_AP_PASSWD, EXAMPLE_ESP_WIFI_CHANNEL);

    return esp_netif_ap;
}

/* Initialize wifi station */
esp_netif_t *wifi_init_sta(void)
{
    esp_netif_t *esp_netif_sta = esp_netif_create_default_wifi_sta();

    wifi_config_t wifi_sta_config = {
        .sta = {
            .ssid = EXAMPLE_ESP_WIFI_STA_SSID,
            .password = EXAMPLE_ESP_WIFI_STA_PASSWD,
            .scan_method = WIFI_ALL_CHANNEL_SCAN,
            .failure_retry_cnt = EXAMPLE_ESP_MAXIMUM_RETRY,
            /* Authmode threshold resets to WPA2 as default if password matches WPA2 standards (password len => 8).
             * If you want to connect the device to deprecated WEP/WPA networks, Please set the threshold value
             * to WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK and set the password with length and format matching to
            * WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK standards.
             */
            .threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD,
            .sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
        },
    };

    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_sta_config) );

    ESP_LOGI(TAG_STA, "wifi_init_sta finished.");

    return esp_netif_sta;
}

esp_err_t weather_http_event_handle_cb(esp_http_client_event_t *evt)
{
    ESP_LOGI(TAG, "HTTP Event %d", evt->event_id);
    return ESP_OK;
}

void get_weather() 
{
    esp_http_client_config_t config = {
        .url = "http://api.seniverse.com/v3/weather/daily.json?key=rrpd2zmqkpwlsckt&location=shenzhen&language=en&unit=c&start=0&days=3",
        //.method = HTTP_METHOD_GET,
    };
    esp_http_client_handle_t client = esp_http_client_init(&config);
    // GET Request
    esp_http_client_set_method(client, HTTP_METHOD_GET);
    esp_err_t err = esp_http_client_open(client, 0);
    if (err != ESP_OK) 
    {
        ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
    } 
    else 
    {
        int content_length = esp_http_client_fetch_headers(client);
        if (content_length < 0) 
        {
            ESP_LOGE(TAG, "HTTP client fetch headers failed");
        } 
        else 
        {
            void *output_buffer = malloc(content_length + 1);
            int data_read = esp_http_client_read_response(client, output_buffer, content_length + 1);
            if (data_read >= 0) 
            {
                ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %d",
                esp_http_client_get_status_code(client),				//获取响应状态信息
                (int)esp_http_client_get_content_length(client));		//获取响应信息长度
                ESP_LOGE(TAG,"data:%s", (char*)output_buffer);                 //打印响应信息
																	
            } 
            else 
            {
                ESP_LOGE(TAG, "Failed to read response");
            }
            free(output_buffer);
        }
    }
    esp_http_client_close(client);
}

typedef struct{
    unsigned char wifi_ssid[32];
    unsigned char wifi_password[64];
}st_wifi_config_t;

//邮箱句柄
static QueueHandle_t wifi_config_queue = NULL;

int wifi_sta_config_set(unsigned char *ssid, unsigned char *password)
{
    if(wifi_config_queue == NULL)
    {
        ESP_LOGE(TAG, "wifi config queue is NULL");
        return -1;
    }

    st_wifi_config_t wifi_config = {0};
    strncpy((char*)wifi_config.wifi_ssid, (char*)ssid, strlen((const char*)ssid));
    strncpy((char*)wifi_config.wifi_password, (char*)password, strlen((const char*)password));

    if(xQueueSend(wifi_config_queue, &wifi_config, 0 / portTICK_PERIOD_MS) != pdTRUE)
    {
        ESP_LOGE(TAG, "Send wifi config failed");
        return -1;
    }
    return 0;
}

void wifi_sta_config_task(void *arg)
{
    wifi_config_queue = xQueueCreate(3, sizeof(st_wifi_config_t));
    if(wifi_config_queue == NULL)
    {
        ESP_LOGE(TAG, "Create wifi config queue failed");
        vTaskDelete(NULL);
        return;
    }

    while (1) {
        st_wifi_config_t wifi_config = {0};
        if(xQueueReceive(wifi_config_queue, &wifi_config, portMAX_DELAY) == pdTRUE)
        {
            ESP_LOGI(TAG, "Received wifi config, ssid:%s, password:%s", wifi_config.wifi_ssid, wifi_config.wifi_password);
            wifi_config_t wifi_sta_config = {
                .sta = {
                    // .ssid = EXAMPLE_ESP_WIFI_STA_SSID,
                    // .password = EXAMPLE_ESP_WIFI_STA_PASSWD,
                    .scan_method = WIFI_ALL_CHANNEL_SCAN,
                    .failure_retry_cnt = EXAMPLE_ESP_MAXIMUM_RETRY,
                    .threshold = {
                        .authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD,
                    },
                    .sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
                },
            };
            strncpy((char*)wifi_sta_config.sta.ssid, (char*)wifi_config.wifi_ssid, strlen((const char*)wifi_config.wifi_ssid));
            strncpy((char*)wifi_sta_config.sta.password, (char*)wifi_config.wifi_password, strlen((const char*)wifi_config.wifi_password));
            esp_wifi_disconnect();
            vTaskDelay(1000 / portTICK_PERIOD_MS); // 等待一段时间以确保断开连接
            ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_sta_config));
            esp_wifi_connect();
            ESP_LOGI(TAG, "wifi_init_sta finished.");
        }
    }
}


void wifi_app_main(void)
{
    //初始化依赖的环境依赖
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    /* Initialize event group */
    s_wifi_event_group = xEventGroupCreate();
    /* Register Event handler */
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                    ESP_EVENT_ANY_ID,
                    &wifi_event_handler,
                    NULL,
                    NULL));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                    IP_EVENT_STA_GOT_IP,
                    &wifi_event_handler,
                    NULL,
                    NULL));

    /*Initialize WiFi */
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    //模式AP、STA同时开启
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));
    /* Initialize AP */
    ESP_LOGI(TAG_AP, "ESP_WIFI_MODE_AP");
    esp_netif_t *esp_netif_ap = wifi_init_softap();
    /* start softAP config wifi server*/
    ESP_ERROR_CHECK(start_web_server());
    /* Initialize STA */
    ESP_LOGI(TAG_STA, "ESP_WIFI_MODE_STA");
    esp_netif_t *esp_netif_sta = wifi_init_sta();
    /* Start WiFi */
    ESP_ERROR_CHECK(esp_wifi_start());
    #if 0
    /*
    * Wait until either the connection is established (WIFI_CONNECTED_BIT) or
    * connection failed for the maximum number of re-tries (WIFI_FAIL_BIT).
    * The bits are set by event_handler() (see above)
    */
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
                                        WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
                                        pdFALSE,
                                        pdFALSE,
                                        3000 / portTICK_PERIOD_MS);

    /* xEventGroupWaitBits() returns the bits before the call returned,
    * hence we can test which event actually happened. */
    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG_STA, "connected to ap SSID:%s password:%s",
                EXAMPLE_ESP_WIFI_STA_SSID, EXAMPLE_ESP_WIFI_STA_PASSWD);
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGI(TAG_STA, "Failed to connect to SSID:%s, password:%s",
                EXAMPLE_ESP_WIFI_STA_SSID, EXAMPLE_ESP_WIFI_STA_PASSWD);
    } else {
        ESP_LOGE(TAG_STA, "UNEXPECTED EVENT");
        return;
    }
    /* Set sta as the default interface */
    int ret = esp_netif_set_default_netif(esp_netif_sta);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG_STA, "Failed to set default interface, netif_sta: %p, ret: %d", esp_netif_sta, ret);
        return;
    }

    /* Enable napt on the AP netif */
    ret = esp_netif_napt_enable(esp_netif_ap);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG_STA, "NAPT not enabled on the netif_ap: %p, ret: %d", esp_netif_ap, ret);
        return;
    }
#endif
    ESP_LOGI(TAG, "Starting wifi_config_task");
    xTaskCreate(wifi_sta_config_task, "wifi_config_task", 4096, NULL, 5, NULL);
}
